///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000-2003 Intel Corporation 
// All rights reserved. 
//
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions are met: 
//
// * Redistributions of source code must retain the above copyright notice, 
// this list of conditions and the following disclaimer. 
// * Redistributions in binary form must reproduce the above copyright notice, 
// this list of conditions and the following disclaimer in the documentation 
// and/or other materials provided with the distribution. 
// * Neither name of Intel Corporation nor the names of its contributors 
// may be used to endorse or promote products derived from this software 
// without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR 
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////
/*TU*
    
    TombUPnP - a library for developing UPnP applications.
    
    Copyright (C) 2006-2010 Sergey 'Jin' Bostandzhyan <jin@mediatomb.cc>
    
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License version 2.1 as published by the Free Software Foundation.
    
    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
    
    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
    
    $Id: httpreadwrite.c 2081 2010-03-23 20:18:00Z lww $
*/

/************************************************************************
* Purpose: This file defines the functionality making use of the http 
* It defines functions to receive messages, process messages, send 
* messages
************************************************************************/
#ifdef HAVE_CONFIG_H
    #include "autoconfig.h"
#endif


#include "config.h"

#include <assert.h>
#include <stdarg.h>
#include <stdio.h>

#ifndef WIN32
 #include <arpa/inet.h>
 #include <netinet/in.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/time.h>
 #include <sys/wait.h>
 #include <unistd.h>
 #include <sys/utsname.h>
 #include <fcntl.h>
 #include <inttypes.h>
#else
 #include <winsock2.h>
 #include <malloc.h>
#endif
#include "unixutil.h"
#include "upnp.h"
#include "upnpapi.h"
#include "membuffer.h"
#include "uri.h"
#include "statcodes.h"
#include "httpreadwrite.h"
#include "sock.h"
#include "webserver.h"

#define DOSOCKET_READ	1
#define DOSOCKET_WRITE	0

/************************************************************************
* Function: http_FixUrl													
*																		
* Parameters:															
*	IN uri_type* url ;			URL to be validated and fixed
*	OUT uri_type* fixed_url ;	URL after being fixed.
*																		
* Description: Validates URL											
*																		
* Returns:																
*	 UPNP_E_INVALID_URL													
* 	 UPNP_E_SUCCESS														
************************************************************************/
int
http_FixUrl( IN uri_type * url,
             OUT uri_type * fixed_url )
{
    char *temp_path = "/";

    *fixed_url = *url;

    if( token_string_casecmp( &fixed_url->scheme, "http" ) != 0 ) {
        return UPNP_E_INVALID_URL;
    }

    if( fixed_url->hostport.text.size == 0 ) {
        return UPNP_E_INVALID_URL;
    }
    // set pathquery to "/" if it is empty
    if( fixed_url->pathquery.size == 0 ) {
        fixed_url->pathquery.buff = temp_path;
        fixed_url->pathquery.size = 1;
    }

    return UPNP_E_SUCCESS;
}

/************************************************************************
* Function: http_FixStrUrl												
*																		
* Parameters:															
*	IN char* urlstr ; 			Character string as a URL											
*	IN int urlstrlen ; 			Length of the character string								
*	OUT uri_type* fixed_url	;	Fixed and corrected URL
*																		
* Description: Parses URL and then validates URL						
*																		
* Returns:																
*	 UPNP_E_INVALID_URL													
* 	 UPNP_E_SUCCESS														
************************************************************************/
int
http_FixStrUrl( IN char *urlstr,
                IN int urlstrlen,
                OUT uri_type * fixed_url )
{
    uri_type url;

    if( parse_uri( urlstr, urlstrlen, &url ) != HTTP_SUCCESS ) {
        return UPNP_E_INVALID_URL;
    }

    return http_FixUrl( &url, fixed_url );
}

/************************************************************************
* Function: http_Connect												
*																		
* Parameters:															
*	IN uri_type* destination_url ; URL containing destination information					
*	OUT uri_type *url ;			   Fixed and corrected URL
*																		
* Description: Gets destination address from URL and then connects to the 
*	remote end
*																		
* Returns:																
*	socket descriptor on sucess											
*	UPNP_E_OUTOF_SOCKET													
*	UPNP_E_SOCKET_CONNECT on error										
************************************************************************/
int
http_Connect( IN uri_type * destination_url,
              OUT uri_type * url )
{
    int connfd;

    http_FixUrl( destination_url, url );

    connfd = socket( AF_INET, SOCK_STREAM, 0 );
    if( connfd == -1 ) {
        return UPNP_E_OUTOF_SOCKET;
    }

    if( connect( connfd, ( struct sockaddr * )&url->hostport.IPv4address,
                 sizeof( struct sockaddr_in ) ) == -1 ) {
#ifdef WIN32
		DBGONLY(
			UpnpPrintf(UPNP_CRITICAL, HTTP, __FILE__, __LINE__,
			"connect error: %d\n", WSAGetLastError());
		)
#endif
        shutdown( connfd, SD_BOTH );
        UpnpCloseSocket( connfd );
        return UPNP_E_SOCKET_CONNECT;
    }

    return connfd;
}

/************************************************************************
* Function: http_RecvMessage											
*																		
* Parameters:															
*	IN SOCKINFO *info ;					Socket information object
*	OUT http_parser_t* parser,			HTTP parser object
*	IN http_method_t request_method ;	HTTP request method					
*	IN OUT int* timeout_secs ;			time out											
*	OUT int* http_error_code ;			HTTP error code returned
*																		
* Description: Get the data on the socket and take actions based on the 
*	read data to modify the parser objects buffer. If an error is reported 
*	while parsing the data, the error code is passed in the http_errr_code 
*	parameter
*																		
* Returns:																
*	 UPNP_E_BAD_HTTPMSG													
* 	 UPNP_E_SUCCESS														
************************************************************************/
int
http_RecvMessage( IN SOCKINFO * info,
                  OUT http_parser_t * parser,
                  IN http_method_t request_method,
                  IN OUT int *timeout_secs,
                  OUT int *http_error_code )
{
    parse_status_t status;
    int num_read;
    xboolean ok_on_close = FALSE;
    char buf[2 * 1024];

    if( request_method == HTTPMETHOD_UNKNOWN ) {
        parser_request_init( parser );
    } else {
        parser_response_init( parser, request_method );
    }

    while( TRUE ) {
        num_read = sock_read( info, buf, sizeof( buf ), timeout_secs );
        if( num_read > 0 ) {
            // got data
            status = parser_append( parser, buf, num_read );

            if( status == PARSE_SUCCESS ) {
                DBGONLY( UpnpPrintf
                         ( UPNP_INFO, HTTP, __FILE__, __LINE__,
                           "<<< (RECVD) <<<\n%s\n-----------------\n",
                           parser->msg.msg.buf );
                         //print_http_headers( &parser->msg );
                     )

                    if( parser->content_length > g_maxContentLength ) {
                    *http_error_code = HTTP_REQ_ENTITY_TOO_LARGE;
                    return UPNP_E_OUTOF_BOUNDS;
                }

                return 0;
            } else if( status == PARSE_FAILURE ) {
                *http_error_code = parser->http_error_code;
                return UPNP_E_BAD_HTTPMSG;
            } else if( status == PARSE_INCOMPLETE_ENTITY ) {
                // read until close
                ok_on_close = TRUE;
            } else if( status == PARSE_CONTINUE_1 ) //Web post request. murari
            {
                return PARSE_SUCCESS;
            }
        } else if( num_read == 0 ) {
            if( ok_on_close ) {
                DBGONLY( UpnpPrintf
                         ( UPNP_INFO, HTTP, __FILE__, __LINE__,
                           "<<< (RECVD) <<<\n%s\n-----------------\n",
                           parser->msg.msg.buf );
                         //print_http_headers( &parser->msg );
                     )

                    return 0;
            } else {
                // partial msg
                *http_error_code = HTTP_BAD_REQUEST;    // or response
                return UPNP_E_BAD_HTTPMSG;
            }
        } else {
            *http_error_code = parser->http_error_code;
            return num_read;
        }
    }
}

/************************************************************************
* Function: http_SendMessage											
*																		
* Parameters:															
*	IN SOCKINFO *info ;		Socket information object
*	IN OUT int * TimeOut ; 	time out value											
*	IN const char* fmt, ...	 Pattern format to take actions upon								
*																		
* Description: Sends a message to the destination based on the			
*	IN const char* fmt parameter										
*	fmt types:															
*		'f':	arg = const char * file name							
*		'b':	arg1 = const char * mem_buffer; arg2= size_t buf_length	
*	E.g.:																
*		char *buf = "POST /xyz.cgi http/1.1\r\n\r\n";					
*		char *filename = "foo.dat";										
*		int status = http_SendMessage( tcpsock, "mf",					
*					buf, strlen(buf),	// args for memory buffer		
*					filename );			// arg for file					
*																		
* Returns:																
*	UPNP_E_OUTOF_MEMORY													
* 	UPNP_E_FILE_READ_ERROR												
*	UPNP_E_SUCCESS														
************************************************************************/
int
http_SendMessage( IN SOCKINFO * info,
                  IN OUT int *TimeOut,
                  IN const char *fmt,
                  ... )
{
#define CHUNK_HEADER_SIZE 10
#define CHUNK_TAIL_SIZE 10

    char c;
    char *buf = NULL;
    size_t buf_length;
    char *filename = NULL;
    UpnpWebFileHandle *Fh = NULL;
    FILE *Fp = NULL;
    off_t num_read = -1,
      num_written,
      amount_to_be_read = 0;
    va_list argp;
    char *file_buf = NULL,
     *ChunkBuf = NULL;
    struct SendInstruction *Instr = NULL;
    char Chunk_Header[10];
    int RetVal = 0;

    // 10 byte allocated for chunk header.
    off_t Data_Buf_Size = WEB_SERVER_BUF_SIZE;

    va_start( argp, fmt );

    while( ( c = *fmt++ ) != 0 ) {
        if( c == 'I' ) {
            Instr = ( struct SendInstruction * )
                va_arg( argp, struct SendInstruction * );

            assert( Instr );

            if( Instr->ReadSendSize >= 0 )
                amount_to_be_read = Instr->ReadSendSize;
            else
                amount_to_be_read = Data_Buf_Size;

            if( amount_to_be_read < WEB_SERVER_BUF_SIZE )
                Data_Buf_Size = amount_to_be_read;

            ChunkBuf = ( char * )malloc( Data_Buf_Size +
                                         CHUNK_HEADER_SIZE +
                                         CHUNK_TAIL_SIZE );
            if( !ChunkBuf )
                return UPNP_E_OUTOF_MEMORY;

            file_buf = ChunkBuf + 10;
        }

        if( c == 'f' ) {        // file name

            filename = ( char * )va_arg( argp, char * );
            Fh = (  UpnpWebFileHandle *)va_arg( argp, UpnpWebFileHandle *);

            if( !(Instr && Instr->IsVirtualFile) )
//            if( Instr && Instr->IsVirtualFile )
//              Fh = (  UpnpWebFileHandle *)va_arg( argp, UpnpWebFileHandle *);
                //Fp = virtualDirCallback.open( filename, UPNP_READ );
//            else
                Fp = fopen( filename, "rb" );

            if( (Fp == NULL) && (Fh == NULL) ){
                free( ChunkBuf );
                return UPNP_E_FILE_READ_ERROR;
            }

//            assert( Fp );

            if( Instr && Instr->IsRangeActive && Instr->IsVirtualFile ) {
                if( virtualDirCallback.seek( Fh, Instr->RangeOffset,
                                             SEEK_CUR ) != 0 ) {
                    free( ChunkBuf );
                    return UPNP_E_FILE_READ_ERROR;
                }
            } else if( Instr && Instr->IsRangeActive ) {
                if( fseeko( Fp, Instr->RangeOffset, SEEK_CUR ) != 0 ) {
                    free( ChunkBuf );
                    return UPNP_E_FILE_READ_ERROR;
                }
            }

            while( amount_to_be_read ) {
                if( Instr ) {
                    if( amount_to_be_read >= Data_Buf_Size ) {
                        if( Instr->IsVirtualFile )
                            num_read = virtualDirCallback.read( Fh,
                                                                file_buf,
                                                                Data_Buf_Size );
                        else
                            num_read = fread( file_buf, 1, Data_Buf_Size,
                                              Fp );
                    } else {
                        if( Instr->IsVirtualFile )
                            num_read = virtualDirCallback.read( Fh,
                                                                file_buf,
                                                                amount_to_be_read );
                        else
                            num_read = fread( file_buf, 1,
                                              amount_to_be_read, Fp );
                    }
                    // HACK: we don't want a read that will block
                    // indefinetely, so we will return -666 in the callback
                    // to indicate that this code should not write to the
                    // socket but just check if the socket is still ok and
                    // then read again
                    if (num_read < 0)
                    {
                        if ((Instr->IsVirtualFile) && (num_read == -666))
                        {
                            if (sock_check_w(info) == 1)
                            {
//                                printf("SOCKET IS OK!\n");
                                continue;
                            }
                        
//                            printf("SOCKET IS NOT OK!\n");
                        }
                        RetVal = UPNP_E_FILE_READ_ERROR; 
                        goto Cleanup_File;
                    }
                    amount_to_be_read = amount_to_be_read - num_read;

                    if( Instr->ReadSendSize < 0 ) {
                        //read until close
                        amount_to_be_read = Data_Buf_Size;
                    }
                } else {
                    num_read = fread( file_buf, 1, Data_Buf_Size, Fp );
                }

                if( num_read == 0 ) // EOF so no more to send.
                {
                    if( Instr && Instr->IsChunkActive ) {
                        num_written = sock_write( info, "0\r\n\r\n",
                                                  strlen( "0\r\n\r\n" ),
                                                  TimeOut );
                    } else {
                        RetVal = UPNP_E_FILE_READ_ERROR;
                    }
                    goto Cleanup_File;
                }
                // Create chunk for the current buffer.
                if( Instr && Instr->IsChunkActive ) {
                    //Copy CRLF at the end of the chunk
                    memcpy( file_buf + num_read, "\r\n", 2 );

                    //Hex length for the chunk size.
                    sprintf( Chunk_Header, "%"PRIx64, (int64_t)num_read );

                    //itoa(num_read,Chunk_Header,16); 
                    strcat( Chunk_Header, "\r\n" );

                    //Copy the chunk size header 
                    memcpy( file_buf - strlen( Chunk_Header ),
                            Chunk_Header, strlen( Chunk_Header ) );

                    // on the top of the buffer.
                    //file_buf[num_read+strlen(Chunk_Header)] = NULL;
                    //printf("Sending %s\n",file_buf-strlen(Chunk_Header));
                    num_written = sock_write( info,
                                              file_buf -
                                              strlen( Chunk_Header ),
                                              num_read +
                                              strlen( Chunk_Header ) + 2,
                                              TimeOut );

                    if( num_written !=
                        num_read + ( int )strlen( Chunk_Header )
                        + 2 ) {
                        goto Cleanup_File;  //Send error nothing we can do.
                    }
                } else {
                    // write data
                    num_written = sock_write( info, file_buf, num_read,
                                              TimeOut );

                    DBGONLY( UpnpPrintf
                             ( UPNP_INFO, HTTP, __FILE__, __LINE__,
                               ">>> (SENT) >>>\n%.*s\n------------\n",
                               ( int )num_written, file_buf );
                         )

                        //Send error nothing we can do
                        if( num_written != num_read ) {
                        goto Cleanup_File;
                    }
                }
            }                   //While
            // patch from the maemo forums
            // https://garage.maemo.org/tracker/index.php?func=detail&aid=88&group_id=74&atid=341
            if( num_read != 0 ) // not really necessary
            {
                if( Instr && Instr->IsChunkActive) {
                    num_written = sock_write( info,
                                              "0\r\n\r\n",
                                              strlen("0\r\n\r\n"),
                                              TimeOut );
                } else {
                    RetVal = UPNP_E_FILE_READ_ERROR;
                }
            }
            // --            
          Cleanup_File:
            va_end( argp );
            if( Instr && Instr->IsVirtualFile )
                virtualDirCallback.close( Fh );
            else
                fclose( Fp );
            free( ChunkBuf );
            return RetVal;

        } else if( c == 'b' ) { // memory buffer

            buf = ( char * )va_arg( argp, char * );

            buf_length = ( size_t ) va_arg( argp, size_t );
            if( buf_length > 0 ) {
                num_written = sock_write( info, buf, buf_length, TimeOut );
                if( ( size_t ) num_written != buf_length )
                    goto end;
                DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
                                     ">>> (SENT) >>>\n%.*s\n------------\n",
                                     ( int )buf_length, buf );
                     )
            }
        }
    }

  end:
    va_end( argp );
    free( ChunkBuf );
    return 0;
}

/************************************************************************
* Function: http_RequestAndResponse										
*																		
* Parameters:															
*	IN uri_type* destination ;		Destination URI object which contains 
*									remote IP address among other elements
*	IN const char* request ;		Request to be sent
*	IN size_t request_length ;		Length of the request
*	IN http_method_t req_method ;	HTTP Request method
*	IN int timeout_secs ;			time out value
*	OUT http_parser_t* response	;	Parser object to receive the repsonse
*																		
* Description: Initiates socket, connects to the destination, sends a	
*	request and waits for the response from the remote end				
*																		
* Returns:																
*	UPNP_E_SOCKET_ERROR													
* 	UPNP_E_SOCKET_CONNECT												
*	Error Codes returned by http_SendMessage							
*	Error Codes returned by http_RecvMessage							
************************************************************************/
int
http_RequestAndResponse( IN uri_type * destination,
                         IN const char *request,
                         IN size_t request_length,
                         IN http_method_t req_method,
                         IN int timeout_secs,
                         OUT http_parser_t * response )
{
    int tcp_connection;
    int ret_code;
    int http_error_code;
    SOCKINFO info;

    tcp_connection = socket( AF_INET, SOCK_STREAM, 0 );
    if( tcp_connection == -1 ) {
        parser_response_init( response, req_method );
        return UPNP_E_SOCKET_ERROR;
    }
    if( sock_init( &info, tcp_connection ) != UPNP_E_SUCCESS )
    {
        sock_destroy( &info, SD_BOTH );
        parser_response_init( response, req_method );
        return UPNP_E_SOCKET_ERROR;
    }
    // connect
    ret_code = connect( info.socket,
                        ( struct sockaddr * )&destination->hostport.
                        IPv4address, sizeof( struct sockaddr_in ) );

    if( ret_code == -1 ) {
        sock_destroy( &info, SD_BOTH );
        parser_response_init( response, req_method );
        return UPNP_E_SOCKET_CONNECT;
    }
    // send request
    ret_code = http_SendMessage( &info, &timeout_secs, "b",
                                 request, (size_t)request_length );
    if( ret_code != 0 ) {
        sock_destroy( &info, SD_BOTH );
        parser_response_init( response, req_method );
        return ret_code;
    }
    // recv response
    ret_code = http_RecvMessage( &info, response, req_method,
                                 &timeout_secs, &http_error_code );

    sock_destroy( &info, SD_BOTH ); //should shutdown completely

    return ret_code;
}

/************************************************************************
*	Function :	http_Download
*
*	Parameters :
*		IN const char* url_str :	String as a URL
*		IN int timeout_secs :		time out value
*		OUT char** document :		buffer to store the document extracted
*									from the donloaded message.
*		OUT int* doc_length :		length of the extracted document
*	    OUT char* content_type :	Type of content
*
*	Description :	Download the document message and extract the document 
*		from the message.
*
*	Return : int;
*		UPNP_E_SUCCESS;
*		UPNP_E_INVALID_URL;
*			
*
*	Note :
************************************************************************/
int
http_Download( IN const char *url_str,
               IN int timeout_secs,
               OUT char **document,
               OUT int *doc_length,
               OUT char *content_type )
{
    int ret_code;
    uri_type url;
    char *msg_start,
     *entity_start,
     *hoststr,
     *temp;
    http_parser_t response;
    size_t msg_length,
      hostlen;
    memptr ctype;
    size_t copy_len;
    membuffer request;
    char *urlPath = alloca( strlen( url_str ) + 1 );

    //ret_code = parse_uri( (char*)url_str, strlen(url_str), &url );
    DBGONLY( UpnpPrintf
             ( UPNP_INFO, HTTP, __FILE__, __LINE__, "DOWNLOAD URL : %s\n",
               url_str );
         )
        ret_code =
        http_FixStrUrl( ( char * )url_str, strlen( url_str ), &url );
    if( ret_code != UPNP_E_SUCCESS ) {
        return ret_code;
    }
    // make msg
    membuffer_init( &request );

    strcpy( urlPath, url_str );
    hoststr = strstr( urlPath, "//" );
    if( hoststr == NULL ) {
        return UPNP_E_INVALID_URL;
    }

    hoststr += 2;
    temp = strchr( hoststr, '/' );
    if( temp == NULL ) {
        return UPNP_E_INVALID_URL;
    }

    *temp = '\0';
    hostlen = strlen( hoststr );
    *temp = '/';
    DBGONLY( UpnpPrintf
             ( UPNP_INFO, HTTP, __FILE__, __LINE__,
               "HOSTNAME : %s Length : %zu\n", hoststr, hostlen );
         )

        ret_code = http_MakeMessage( &request, 1, 1, "QsbcDCUc",
                                     HTTPMETHOD_GET, url.pathquery.buff,
                                     (size_t)url.pathquery.size, "HOST: ", 
                                     hoststr, (size_t)hostlen );
    if( ret_code != 0 ) {
        DBGONLY( UpnpPrintf
                 ( UPNP_INFO, HTTP, __FILE__, __LINE__,
                   "HTTP Makemessage failed\n" );
             )
            membuffer_destroy( &request );
        return ret_code;
    }

    DBGONLY( UpnpPrintf
             ( UPNP_INFO, HTTP, __FILE__, __LINE__,
               "HTTP Buffer:\n %s\n----------END--------\n", request.buf );
         )
        // get doc msg
        ret_code =
        http_RequestAndResponse( &url, request.buf, request.length,
                                 HTTPMETHOD_GET, timeout_secs, &response );

    if( ret_code != 0 ) {
        httpmsg_destroy( &response.msg );
        membuffer_destroy( &request );
        return ret_code;
    }

    DBGONLY( UpnpPrintf
             ( UPNP_INFO, HTTP, __FILE__, __LINE__, "Response\n" );
         )
        DBGONLY( print_http_headers( &response.msg );
         )

        // optional content-type
        if( content_type ) {
        if( httpmsg_find_hdr( &response.msg, HDR_CONTENT_TYPE, &ctype ) ==
            NULL ) {
            *content_type = '\0';   // no content-type
        } else {
            // safety
            copy_len = ctype.length < LINE_SIZE - 1 ?
                ctype.length : LINE_SIZE - 1;

            memcpy( content_type, ctype.buf, copy_len );
            content_type[copy_len] = '\0';
        }
    }
    //
    // extract doc from msg
    //

    if( ( *doc_length = ( int )response.msg.entity.length ) == 0 ) {
        // 0-length msg
        *document = NULL;
    } else if( response.msg.status_code == HTTP_OK )    //LEAK_FIX_MK
    {
        // copy entity
        entity_start = response.msg.entity.buf; // what we want
        msg_length = response.msg.msg.length;   // save for posterity   
        msg_start = membuffer_detach( &response.msg.msg );  // whole msg

        // move entity to the start; copy null-terminator too
        memmove( msg_start, entity_start, *doc_length + 1 );

        // save mem for body only
        *document = realloc( msg_start, *doc_length + 1 );  //LEAK_FIX_MK
        //*document = Realloc( msg_start,msg_length, *doc_length + 1 );//LEAK_FIX_MK

        // shrink can't fail
        assert( ( int )msg_length > *doc_length );
        assert( *document != NULL );
    }

    if( response.msg.status_code == HTTP_OK ) {
        ret_code = 0;           // success
    } else {
        // server sent error msg (not requested doc)
        ret_code = response.msg.status_code;
    }

    httpmsg_destroy( &response.msg );
    membuffer_destroy( &request );

    return ret_code;
}

typedef struct HTTPPOSTHANDLE {
    SOCKINFO sock_info;
    int contentLength;
} http_post_handle_t;

/************************************************************************
* Function: MakePostMessage												
*																		
* Parameters:															
*	const char *url_str ;		String as a URL
*	membuffer *request ;		Buffer containing the request									
*	uri_type *url ; 			URI object containing the scheme, path 
*								query token, etc.
*	off_t contentLength ;			length of content
*	const char *contentType	;	Type of content
*																		
* Description: Makes the message for the HTTP POST message				
*																		
* Returns:																
*	UPNP_E_INVALID_URL													
* 	UPNP_E_INVALID_PARAM												
*	UPNP_E_SUCCESS														
************************************************************************/
int
MakePostMessage( const char *url_str,
                 membuffer * request,
                 uri_type * url,
                 off_t contentLength,
                 const char *contentType )
{
    int ret_code = 0;
    char *urlPath = alloca( strlen( url_str ) + 1 );
    int hostlen = 0;
    char *hoststr,
     *temp;

    DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
                         "DOWNLOAD URL : %s\n", url_str );
         )

        ret_code =
        http_FixStrUrl( ( char * )url_str, strlen( url_str ), url );

    if( ret_code != UPNP_E_SUCCESS ) {
        return ret_code;
    }
    // make msg
    membuffer_init( request );

    strcpy( urlPath, url_str );
    hoststr = strstr( urlPath, "//" );
    if( hoststr == NULL ) {
        return UPNP_E_INVALID_URL;
    }

    hoststr += 2;
    temp = strchr( hoststr, '/' );
    if( temp == NULL ) {
        return UPNP_E_INVALID_URL;
    }

    *temp = '\0';
    hostlen = strlen( hoststr );
    *temp = '/';
    DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
                         "HOSTNAME : %s Length : %d\n", hoststr, hostlen );
         )

        if( contentLength >= 0 ) {
        ret_code = http_MakeMessage( request, 1, 1, "QsbcDCUTNc",
                                     HTTPMETHOD_POST, url->pathquery.buff,
                                     (size_t)(url->pathquery.size), "HOST: ",
                                     hoststr, (size_t)hostlen, contentType,
                                     contentLength );
    } else if( contentLength == UPNP_USING_CHUNKED ) {
        ret_code = http_MakeMessage( request, 1, 1, "QsbcDCUTKc",
                                     HTTPMETHOD_POST, url->pathquery.buff,
                                     (size_t)(url->pathquery.size), "HOST: ",
                                     hoststr, (size_t)hostlen, contentType );
    } else if( contentLength == UPNP_UNTIL_CLOSE ) {
        ret_code = http_MakeMessage( request, 1, 1, "QsbcDCUTc",
                                     HTTPMETHOD_POST, url->pathquery.buff,
                                     (size_t)(url->pathquery.size), "HOST: ",
                                     hoststr, (size_t)hostlen, contentType );
    } else {
        ret_code = UPNP_E_INVALID_PARAM;
    }

    if( ret_code != 0 ) {
        DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
                             "HTTP Makemessage failed\n" );
             )
            membuffer_destroy( request );
        return ret_code;
    }

    DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
                         "HTTP Buffer:\n %s\n" "----------END--------\n",
                         request->buf );
         )

        return UPNP_E_SUCCESS;
}

/************************************************************************
*	Function :	http_WriteHttpPost
*
*	Parameters :
*		IN void *Handle :		Handle to the http post object
*		IN char *buf :			Buffer to send to peer, if format used
*								is not UPNP_USING_CHUNKED, 
*		IN off_t *size :	Size of the data to be sent.
*		IN int timeout :		time out value
*
*	Description :	Formats data if format used is UPNP_USING_CHUNKED.
*		Writes data on the socket connected to the peer.
*
*	Return : int ;
*		UPNP_E_SUCCESS - On Success
*		UPNP_E_INVALID_PARAM - Invalid Parameter
*		-1 - On Socket Error.
*
*	Note :
************************************************************************/
int
http_WriteHttpPost( IN void *Handle,
                    IN char *buf,
                    IN off_t *size,
                    IN int timeout )
{
    http_post_handle_t *handle = ( http_post_handle_t * ) Handle;
    char *tempbuf = NULL;
    int tempbufSize = 0;
    int freeTempbuf = 0;
    int numWritten = 0;

    if( ( !handle ) || ( !size ) || ( ( ( *size ) > 0 ) && !buf )
        || ( ( *size ) < 0 ) ) {
        if(size) ( *size ) = 0;
        return UPNP_E_INVALID_PARAM;
    }
    if( handle->contentLength == UPNP_USING_CHUNKED ) {
        if( ( *size ) ) {
            int tempSize = 0;

            tempbuf =
                ( char * )malloc( ( *size ) + CHUNK_HEADER_SIZE +
                                  CHUNK_TAIL_SIZE );

            if ( tempbuf == NULL) return UPNP_E_OUTOF_MEMORY;

            sprintf( tempbuf, "%"PRIx64 "\r\n", (int64_t)( *size ) );    //begin chunk
            tempSize = strlen( tempbuf );
            memcpy( tempbuf + tempSize, buf, ( *size ) );
            memcpy( tempbuf + tempSize + ( *size ), "\r\n", 2 );    //end of chunk
            tempbufSize = tempSize + ( *size ) + 2;
            freeTempbuf = 1;
        }
    } else {
        tempbuf = buf;
        tempbufSize = ( *size );
    }

    numWritten =
        sock_write( &handle->sock_info, tempbuf, tempbufSize, &timeout );
    //(*size) = sock_write(&handle->sock_info,tempbuf,tempbufSize,&timeout);

    if( freeTempbuf ) {
        free( tempbuf );
    }
    if( numWritten < 0 ) {
        ( *size ) = 0;
        return numWritten;
    } else {
        ( *size ) = numWritten;
        return UPNP_E_SUCCESS;
    }
}

/************************************************************************
*	Function :	http_CloseHttpPost
*
*	Parameters :
*		IN void *Handle :			Handle to the http post object
*		IN OUT int *httpStatus :	HTTP status returned on receiving a
*									response message
*		IN int timeout :			time out value
*
*	Description :	Sends remaining data if using  UPNP_USING_CHUNKED 
*		format. Receives any more messages. Destroys socket and any socket
*		associated memory. Frees handle associated with the HTTP POST msg.
*
*	Return : int ;
*		UPNP_E_SUCCESS - On Sucess ;
*		UPNP_E_INVALID_PARAM  - Invalid Parameter;
*
*	Note :
************************************************************************/
int
http_CloseHttpPost( IN void *Handle,
                    IN OUT int *httpStatus,
                    IN int timeout )
{
    int retc = 0;
    http_parser_t response;
    int http_error_code;

    http_post_handle_t *handle = Handle;

    if( ( !handle ) || ( !httpStatus ) ) {
        return UPNP_E_INVALID_PARAM;
    }

    if( handle->contentLength == UPNP_USING_CHUNKED ) {
        retc = sock_write( &handle->sock_info, "0\r\n\r\n", strlen( "0\r\n\r\n" ), &timeout );  //send last chunk
    }
    //read response
    parser_response_init( &response, HTTPMETHOD_POST );

    retc =
        http_RecvMessage( &handle->sock_info, &response, HTTPMETHOD_POST,
                          &timeout, &http_error_code );

    ( *httpStatus ) = http_error_code;

    sock_destroy( &handle->sock_info, SD_BOTH );    //should shutdown completely

    httpmsg_destroy( &response.msg );
    free( handle );

    return retc;
}

/************************************************************************
*	Function :	http_OpenHttpPost
*
*	Parameters :
*		IN const char *url_str :		String as a URL	
*		IN OUT void **Handle :			Pointer to buffer to store HTTP
*										post handle
*		IN const char *contentType :	Type of content
*		IN off_t contentLength :			length of content
*		IN int timeout :				time out value
*
*	Description :	Makes the HTTP POST message, connects to the peer, 
*		sends the HTTP POST request. Adds the post handle to buffer of 
*		such handles
*
*	Return : int;
*		UPNP_E_SUCCESS - On Sucess ;
*		UPNP_E_INVALID_PARAM - Invalid Paramter ;
*		UPNP_E_OUTOF_MEMORY ;
*		UPNP_E_SOCKET_ERROR ;
*		UPNP_E_SOCKET_CONNECT ;
*
*	Note :
************************************************************************/
int
http_OpenHttpPost( IN const char *url_str,
                   IN OUT void **Handle,
                   IN const char *contentType,
                   IN off_t contentLength,
                   IN int timeout )
{
    int ret_code;
    int tcp_connection;
    membuffer request;
    http_post_handle_t *handle = NULL;
    uri_type url;

    if( ( !url_str ) || ( !Handle ) || ( !contentType ) ) {
        return UPNP_E_INVALID_PARAM;
    }

    ( *Handle ) = handle;

    if( ( ret_code =
          MakePostMessage( url_str, &request, &url, contentLength,
                           contentType ) ) != UPNP_E_SUCCESS ) {
        return ret_code;
    }

    handle =
        ( http_post_handle_t * ) malloc( sizeof( http_post_handle_t ) );

    if( handle == NULL ) {
        return UPNP_E_OUTOF_MEMORY;
    }

    handle->contentLength = contentLength;

    tcp_connection = socket( AF_INET, SOCK_STREAM, 0 );
    if( tcp_connection == -1 ) {
        ret_code = UPNP_E_SOCKET_ERROR;
        goto errorHandler;
    }

    if( sock_init( &handle->sock_info, tcp_connection ) != UPNP_E_SUCCESS )
    {
        sock_destroy( &handle->sock_info, SD_BOTH );
        ret_code = UPNP_E_SOCKET_ERROR;
        goto errorHandler;
    }

    ret_code = connect( handle->sock_info.socket,
                        ( struct sockaddr * )&url.hostport.IPv4address,
                        sizeof( struct sockaddr_in ) );

    if( ret_code == -1 ) {
        sock_destroy( &handle->sock_info, SD_BOTH );
        ret_code = UPNP_E_SOCKET_CONNECT;
        goto errorHandler;
    }
    // send request
    ret_code = http_SendMessage( &handle->sock_info, &timeout, "b",
                                 request.buf, (size_t)request.length );
    if( ret_code != 0 ) {
        sock_destroy( &handle->sock_info, SD_BOTH );
    }

  errorHandler:
    membuffer_destroy( &request );
    ( *Handle ) = handle;
    return ret_code;
}

typedef struct HTTPGETHANDLE {
    http_parser_t response;
    SOCKINFO sock_info;
    int entity_offset;
    int cancel;
} http_get_handle_t;

/************************************************************************
* Function: MakeGetMessage												
*																		
* Parameters:															
*	const char *url_str ;	String as a URL
*	const char *proxy_str ;	String as a URL of proxy to use
*	membuffer *request ;	Buffer containing the request									
*	uri_type *url ; 		URI object containing the scheme, path 
*							query token, etc.
*																		
* Description: Makes the message for the HTTP GET method				
*																		
* Returns:																
*	UPNP_E_INVALID_URL													
* 	Error Codes returned by http_MakeMessage							
*	UPNP_E_SUCCESS														
************************************************************************/
int
MakeGetMessage( const char *url_str,
                const char *proxy_str,
                membuffer * request,
                uri_type * url )
{
    int ret_code;
    char *urlPath = alloca( strlen( url_str ) + 1 );
    int querylen = 0;
    const char *querystr;
    int hostlen = 0;
    char *hoststr,
     *temp;

    DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
                         "DOWNLOAD URL : %s\n", url_str );
         )

        ret_code =
        http_FixStrUrl( ( char * )url_str, strlen( url_str ), url );

    if( ret_code != UPNP_E_SUCCESS ) {
        return ret_code;
    }
    // make msg
    membuffer_init( request );

    strcpy( urlPath, url_str );
    hoststr = strstr( urlPath, "//" );
    if( hoststr == NULL ) {
        return UPNP_E_INVALID_URL;
    }

    hoststr += 2;
    temp = strchr( hoststr, '/' );
    if( temp == NULL ) {
        return UPNP_E_INVALID_URL;
    }

    *temp = '\0';
    hostlen = strlen( hoststr );
    *temp = '/';
    DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
                         "HOSTNAME : %s Length : %d\n", hoststr, hostlen );
         )

    if( proxy_str ) {
        querystr = url_str;
        querylen = strlen( querystr );
    } else {
        querystr = url->pathquery.buff;
        querylen = url->pathquery.size;
    }

    ret_code = http_MakeMessage( request, 1, 1, "QsbcDCUc",
                                 HTTPMETHOD_GET, querystr, (size_t)querylen,
                                 "HOST: ", hoststr, (size_t)hostlen );

    if( ret_code != 0 ) {
        DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
                             "HTTP Makemessage failed\n" );
             )
            membuffer_destroy( request );
        return ret_code;
    }

    DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
                         "HTTP Buffer:\n %s\n" "----------END--------\n",
                         request->buf );
         )

        return UPNP_E_SUCCESS;
}

/************************************************************************
*	Function :	ReadResponseLineAndHeaders
*
*	Parameters :
*		IN SOCKINFO *info ;				Socket information object
*		IN OUT http_parser_t *parser ;	HTTP Parser object
*		IN OUT int *timeout_secs ;		time out value
*		IN OUT int *http_error_code ;	HTTP errror code returned
*
*	Description : Parses already exiting data. If not complete reads more 
*		data on the connected socket. The read data is then parsed. The 
*		same methid is carried out for headers.
*
*	Return : int ;
*		PARSE_OK - On Success
*		PARSE_FAILURE - Failure to parse data correctly
*		UPNP_E_BAD_HTTPMSG - Socker read() returns an error
*
*	Note :
************************************************************************/
int
ReadResponseLineAndHeaders( IN SOCKINFO * info,
                            IN OUT http_parser_t * parser,
                            IN OUT int *timeout_secs,
                            IN OUT int *http_error_code )
{
    parse_status_t status;
    int num_read;
    char buf[2 * 1024];
    int done = 0;
    int ret_code = 0;

    //read response line

    status = parser_parse_responseline( parser );
    if( status == PARSE_OK ) {
        done = 1;
    } else if( status == PARSE_INCOMPLETE ) {
        done = 0;
    } else {
        //error
        return status;
    }

    while( !done ) {
        num_read = sock_read( info, buf, sizeof( buf ), timeout_secs );
        if( num_read > 0 ) {
            // append data to buffer
            ret_code = membuffer_append( &parser->msg.msg, buf, (size_t)num_read );
            if( ret_code != 0 ) {
                // set failure status
                parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
                return PARSE_FAILURE;
            }
            status = parser_parse_responseline( parser );
            if( status == PARSE_OK ) {
                done = 1;
            } else if( status == PARSE_INCOMPLETE ) {
                done = 0;
            } else {
                //error
                return status;
            }
        } else if( num_read == 0 ) {

            // partial msg
            *http_error_code = HTTP_BAD_REQUEST;    // or response
            return UPNP_E_BAD_HTTPMSG;

        } else {
            *http_error_code = parser->http_error_code;
            return num_read;
        }
    }

    done = 0;

    status = parser_parse_headers( parser );
    if( ( status == PARSE_OK ) && ( parser->position == POS_ENTITY ) ) {

        done = 1;
    } else if( status == PARSE_INCOMPLETE ) {
        done = 0;
    } else {
        //error
        return status;
    }

    //read headers
    while( !done ) {
        num_read = sock_read( info, buf, sizeof( buf ), timeout_secs );
        if( num_read > 0 ) {
            // append data to buffer
            ret_code = membuffer_append( &parser->msg.msg, buf, (size_t)num_read );
            if( ret_code != 0 ) {
                // set failure status
                parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
                return PARSE_FAILURE;
            }
            status = parser_parse_headers( parser );
            if( ( status == PARSE_OK )
                && ( parser->position == POS_ENTITY ) ) {

                done = 1;
            } else if( status == PARSE_INCOMPLETE ) {
                done = 0;
            } else {
                //error
                return status;
            }
        } else if( num_read == 0 ) {

            // partial msg
            *http_error_code = HTTP_BAD_REQUEST;    // or response
            return UPNP_E_BAD_HTTPMSG;

        } else {
            *http_error_code = parser->http_error_code;
            return num_read;
        }
    }

    return PARSE_OK;
}

/************************************************************************
*	Function :	http_ReadHttpGet
*
*	Parameters :
*		IN void *Handle :			Handle to the HTTP get object
*		IN OUT char *buf :			Buffer to get the read and parsed data
*		IN OUT off_t *size :	Size of the buffer passed
*		IN int timeout :			time out value
*
*	Description :	Parses already existing data, then gets new data.
*		Parses and extracts information from the new data.
*
*	Return : int ;
*		UPNP_E_SUCCESS - On Sucess ;
*		UPNP_E_INVALID_PARAM  - Invalid Parameter;
*		UPNP_E_BAD_RESPONSE ;
*		UPNP_E_BAD_HTTPMSG ;
*		UPNP_E_CANCELED ;
*
*	Note :
************************************************************************/
int
http_ReadHttpGet( IN void *Handle,
                  IN OUT char *buf,
                  IN OUT off_t *size,
                  IN int timeout )
{
    http_get_handle_t *handle = Handle;

    parse_status_t status;
    int num_read;
    xboolean ok_on_close = FALSE;
    char tempbuf[2 * 1024];

    int ret_code = 0;

    if( ( !handle ) || ( !size ) || ( ( ( *size ) > 0 ) && !buf )
        || ( ( *size ) < 0 ) ) {
        if(size) ( *size ) = 0;
        return UPNP_E_INVALID_PARAM;
    }
    //first parse what has already been gotten
    if( handle->response.position != POS_COMPLETE ) {
        status = parser_parse_entity( &handle->response );
    } else {
        status = PARSE_SUCCESS;
    }

    if( status == PARSE_INCOMPLETE_ENTITY ) {
        // read until close
        ok_on_close = TRUE;
    } else if( ( status != PARSE_SUCCESS )
               && ( status != PARSE_CONTINUE_1 )
               && ( status != PARSE_INCOMPLETE ) ) {
        //error
        ( *size ) = 0;
        return UPNP_E_BAD_RESPONSE;
    }
    //read more if necessary entity
    while( ( ( handle->entity_offset + ( *size ) ) >
             handle->response.msg.entity.length )
           && ( ! handle->cancel )
           && ( handle->response.position != POS_COMPLETE ) ) {
        num_read =
            sock_read( &handle->sock_info, tempbuf, sizeof( tempbuf ),
                       &timeout );
        if( num_read > 0 ) {
            // append data to buffer
            ret_code = membuffer_append( &handle->response.msg.msg,
                                         tempbuf, (size_t)num_read );
            if( ret_code != 0 ) {
                // set failure status
                handle->response.http_error_code =
                    HTTP_INTERNAL_SERVER_ERROR;
                ( *size ) = 0;
                return PARSE_FAILURE;
            }
            status = parser_parse_entity( &handle->response );
            if( status == PARSE_INCOMPLETE_ENTITY ) {
                // read until close
                ok_on_close = TRUE;
            } else if( ( status != PARSE_SUCCESS )
                       && ( status != PARSE_CONTINUE_1 )
                       && ( status != PARSE_INCOMPLETE ) ) {
                //error
                ( *size ) = 0;
                return UPNP_E_BAD_RESPONSE;
            }
        } else if( num_read == 0 ) {
            if( ok_on_close ) {
                DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
                                     "<<< (RECVD) <<<\n%s\n-----------------\n",
                                     handle->response.msg.msg.buf );
                         //print_http_headers( &parser->msg );
                     )
                    handle->response.position = POS_COMPLETE;
            } else {
                // partial msg
                ( *size ) = 0;
                handle->response.http_error_code = HTTP_BAD_REQUEST;    // or response
                return UPNP_E_BAD_HTTPMSG;
            }
        } else {
            ( *size ) = 0;
            return num_read;
        }
    }

    if( ( handle->entity_offset + ( *size ) ) >
        handle->response.msg.entity.length ) {
        ( *size ) =
            handle->response.msg.entity.length - handle->entity_offset;
    }

    memcpy( buf,
            &handle->response.msg.msg.buf[handle->
                                          response.entity_start_position +
                                          handle->entity_offset],
            ( *size ) );
    handle->entity_offset += ( *size );

    if ( handle->cancel )
        return UPNP_E_CANCELED;

    return UPNP_E_SUCCESS;
}

/************************************************************************
*	Function :	http_HttpGetProgress
*
*	Parameters :
*		IN void *Handle :			Handle to the HTTP get object
*		OUT off_t *length :	Buffer to get the read and parsed data
*		OUT off_t *total :	Size of tge buffer passed
*
*	Description :	Extracts information from the Handle to the HTTP get
*					object.
*
*	Return : int ;
*		UPNP_E_SUCCESS - On Sucess ;
*		UPNP_E_INVALID_PARAM  - Invalid Parameter;
*
*	Note :
************************************************************************/
int http_HttpGetProgress( IN void *Handle, 
                      OUT off_t *length,
                      OUT off_t *total )
{
    http_get_handle_t *handle = Handle;

    if( ( !handle ) || ( !length ) || ( !total ) ) {
        return UPNP_E_INVALID_PARAM;
    }
    *length = handle->response.msg.entity.length;
    *total = handle->response.content_length;
    return UPNP_E_SUCCESS;
}

/************************************************************************
*	Function :	http_CancelHttpGet
*
*	Parameters :
*		IN void *Handle ;	Handle to HTTP get object
*
*	Description :	Set the cancel flag of the HttpGet handle
*
*	Return : int ;
*		UPNP_E_SUCCESS - On Success
*		UPNP_E_INVALID_PARAM - Invalid Parameter
*
*	Note :
************************************************************************/
int
http_CancelHttpGet( IN void *Handle )
{
    http_get_handle_t *handle = Handle;

    if( !handle ) {
        return UPNP_E_INVALID_PARAM;
    }

    handle->cancel = 1;

    return UPNP_E_SUCCESS;
}


/************************************************************************
*	Function :	http_CloseHttpGet
*
*	Parameters :
*		IN void *Handle ;	Handle to HTTP get object
*
*	Description :	Clears the handle allocated for the HTTP GET operation
*		Clears socket states and memory allocated for socket operations. 
*
*	Return : int ;
*		UPNP_E_SUCCESS - On Success
*		UPNP_E_INVALID_PARAM - Invalid Parameter
*
*	Note :
************************************************************************/
int
http_CloseHttpGet( IN void *Handle )
{
    http_get_handle_t *handle = Handle;

    if( !handle ) {
        return UPNP_E_INVALID_PARAM;
    }

    sock_destroy( &handle->sock_info, SD_BOTH );    //should shutdown completely
    httpmsg_destroy( &handle->response.msg );
    handle->entity_offset = 0;
    free( handle );
    return UPNP_E_SUCCESS;
}

/************************************************************************
*	Function :	http_OpenHttpGet
*
*	Parameters :
*		IN const char *url_str :	String as a URL
*		IN OUT void **Handle :		Pointer to buffer to store HTTP
*									post handle
*		IN OUT char **contentType :	Type of content
*		OUT off_t *contentLength :	length of content
*		OUT int *httpStatus :		HTTP status returned on receiving a
*									response message
*		IN int timeout :			time out value
*
*	Description :	Makes the HTTP GET message, connects to the peer, 
*		sends the HTTP GET request, gets the response and parses the 
*		response.
*
*	Return : int;
*		UPNP_E_SUCCESS - On Success ;
*		UPNP_E_INVALID_PARAM - Invalid Paramters ;
*		UPNP_E_OUTOF_MEMORY ;
*		UPNP_E_SOCKET_ERROR ;
*		UPNP_E_BAD_RESPONSE ;
*
*	Note :
*
************************************************************************/
int
http_OpenHttpGet( IN const char *url_str,
                  IN OUT void **Handle,
                  IN OUT char **contentType,
                  OUT off_t *contentLength,
                  OUT int *httpStatus,
                  IN int timeout )
{
    return http_OpenHttpGetProxy(url_str, NULL, Handle, contentType, contentLength, httpStatus, timeout);
}

/************************************************************************
*	Function :	http_OpenHttpGetProxy
*
*	Parameters :
*		IN const char *url_str :	String as a URL
*		IN const char *proxy_str :	String as a URL
*		IN OUT void **Handle :		Pointer to buffer to store HTTP
*									post handle
*		IN OUT char **contentType :	Type of content
*		OUT off_t *contentLength :	length of content
*		OUT int *httpStatus :		HTTP status returned on receiving a
*									response message
*		IN int timeout :			time out value
*
*	Description :	Makes the HTTP GET message, connects to the peer, 
*		sends the HTTP GET request, gets the response and parses the 
*		response.
*		If a proxy URL is defined then the connection is made there.
*
*	Return : int;
*		UPNP_E_SUCCESS - On Success ;
*		UPNP_E_INVALID_PARAM - Invalid Paramters ;
*		UPNP_E_OUTOF_MEMORY ;
*		UPNP_E_SOCKET_ERROR ;
*		UPNP_E_BAD_RESPONSE ;
*
*	Note :
*
************************************************************************/
int
http_OpenHttpGetProxy( IN const char *url_str,
                  IN const char *proxy_str,
                  IN OUT void **Handle,
                  IN OUT char **contentType,
                  OUT off_t *contentLength,
                  OUT int *httpStatus,
                  IN int timeout )
{
    int ret_code;
    int http_error_code;
    memptr ctype;
    int tcp_connection;
    membuffer request;
    http_get_handle_t *handle = NULL;
    uri_type url;
    uri_type proxy;
    uri_type *peer;
    parse_status_t status;

    if( ( !url_str ) || ( !Handle ) || ( !contentType )
        || ( !httpStatus ) ) {
        return UPNP_E_INVALID_PARAM;
    }

    ( *httpStatus ) = 0;
    ( *Handle ) = handle;
    ( *contentType ) = NULL;
    ( *contentLength ) = 0;

    if( ( ret_code =
          MakeGetMessage( url_str, proxy_str, &request, &url ) ) != UPNP_E_SUCCESS ) {
        return ret_code;
    }
    if( proxy_str ) {
        ret_code = http_FixStrUrl( ( char * )proxy_str, strlen( proxy_str ), &proxy );
        peer = &proxy;
    } else {
        peer = &url;
    }

    handle = ( http_get_handle_t * ) malloc( sizeof( http_get_handle_t ) );

    if( handle == NULL ) {
        return UPNP_E_OUTOF_MEMORY;
    }

    handle->entity_offset = 0;
    handle->cancel = 0;
    parser_response_init( &handle->response, HTTPMETHOD_GET );

    tcp_connection = socket( AF_INET, SOCK_STREAM, 0 );
    if( tcp_connection == -1 ) {
        ret_code = UPNP_E_SOCKET_ERROR;
        goto errorHandler;
    }

    if( sock_init( &handle->sock_info, tcp_connection ) != UPNP_E_SUCCESS )
    {
        sock_destroy( &handle->sock_info, SD_BOTH );
        ret_code = UPNP_E_SOCKET_ERROR;
        goto errorHandler;
    }

    ret_code = connect( handle->sock_info.socket,
                        ( struct sockaddr * )&peer->hostport.IPv4address,
                        sizeof( struct sockaddr_in ) );

    if( ret_code == -1 ) {
        sock_destroy( &handle->sock_info, SD_BOTH );
        ret_code = UPNP_E_SOCKET_CONNECT;
        goto errorHandler;
    }
    // send request
    ret_code = http_SendMessage( &handle->sock_info, &timeout, "b",
                                 request.buf, (size_t)request.length );
    if( ret_code != 0 ) {
        sock_destroy( &handle->sock_info, SD_BOTH );
        goto errorHandler;
    }

    status =
        ReadResponseLineAndHeaders( &handle->sock_info, &handle->response,
                                    &timeout, &http_error_code );

    if( status != PARSE_OK ) {
        ret_code = UPNP_E_BAD_RESPONSE;
        goto errorHandler;
    }

    status = parser_get_entity_read_method( &handle->response );

    if( ( status != PARSE_CONTINUE_1 ) && ( status != PARSE_SUCCESS ) ) {
        ret_code = UPNP_E_BAD_RESPONSE;
        goto errorHandler;
    }

    ( *httpStatus ) = handle->response.msg.status_code;
    ret_code = UPNP_E_SUCCESS;

    if( httpmsg_find_hdr( &handle->response.msg, HDR_CONTENT_TYPE, &ctype )
        == NULL ) {
        *contentType = NULL;    // no content-type
    } else {
        *contentType = ctype.buf;
    }

    if( handle->response.position == POS_COMPLETE ) {
        ( *contentLength ) = 0;
    } else if( handle->response.ent_position == ENTREAD_USING_CHUNKED ) {
        ( *contentLength ) = UPNP_USING_CHUNKED;
    } else if( handle->response.ent_position == ENTREAD_USING_CLEN ) {
        ( *contentLength ) = handle->response.content_length;
    } else if( handle->response.ent_position == ENTREAD_UNTIL_CLOSE ) {
        ( *contentLength ) = UPNP_UNTIL_CLOSE;
    }

  errorHandler:

    ( *Handle ) = handle;

    membuffer_destroy( &request );

    if( ret_code != UPNP_E_SUCCESS ) {
        httpmsg_destroy( &handle->response.msg );
    }
    return ret_code;
}

/************************************************************************
*	Function :	http_SendStatusResponse
*
*	Parameters :
*		IN SOCKINFO *info :				Socket information object
*		IN int http_status_code :		error code returned while making 
*										or sending the response message
*		IN int request_major_version :	request major version
*		IN int request_minor_version :	request minor version
*
*	Description :	Generate a response message for the status query and
*		send the status response.
*
*	Return : int;
*		0 -- success
*		UPNP_E_OUTOF_MEMORY
*		UPNP_E_SOCKET_WRITE
*		UPNP_E_TIMEDOUT
*
*	Note :
************************************************************************/
int
http_SendStatusResponse( IN SOCKINFO * info,
                         IN int http_status_code,
                         IN int request_major_version,
                         IN int request_minor_version )
{
    int response_major,
      response_minor;
    membuffer membuf;
    int ret;
    int timeout;

    http_CalcResponseVersion( request_major_version, request_minor_version,
                              &response_major, &response_minor );

    membuffer_init( &membuf );
    membuf.size_inc = 70;

    ret = http_MakeMessage( &membuf, response_major, response_minor, "RSCB", http_status_code,  // response start line
                            http_status_code ); // body

    if( ret == 0 ) {
        timeout = HTTP_DEFAULT_TIMEOUT;
        ret = http_SendMessage( info, &timeout, "b",
                                membuf.buf, (size_t)membuf.length );
    }

    membuffer_destroy( &membuf );

    return ret;
}

/************************************************************************
*	Function :	http_MakeMessage
*
*	Parameters :
*		INOUT membuffer* buf :		buffer with the contents of the 
*									message
*		IN int http_major_version :	HTTP major version
*		IN int http_minor_version :	HTTP minor version
*		IN const char* fmt :		Pattern format 
*		... :	
*
*	Description :	Generate an HTTP message based on the format that is 
*		specified in the input parameters.
*
*		fmt types:
*		's':	arg = const char* C_string
*		'b':	arg1 = const char* buf; arg2 = size_t buf_length 
*				memory ptr
*		'c':	(no args) appends CRLF "\r\n"
*		'd':	arg = int number		// appends decimal number
*		'h':	arg = off_t number		// appends off_t number
*		't':	arg = time_t * gmt_time	// appends time in RFC 1123 fmt
*		'D':	(no args) appends HTTP DATE: header
*		'S':	(no args) appends HTTP SERVER: header
*		'U':	(no args) appends HTTP USER-AGENT: header
*		'C':	(no args) appends a HTTP CONNECTION: close header 
*				depending on major,minor version
*		'N':	arg1 = off_t content_length	// content-length header
*		'Q':	arg1 = http_method_t; arg2 = char* url; 
*				arg3 = int url_length // start line of request
*		'R':	arg = int status_code // adds a response start line
*		'B':	arg = int status_code 
*				appends content-length, content-type and HTML body for given code
*		'T':	arg = char * content_type; format e.g: "text/html";	
*				 content-type header
*       'H':    arg = *userHTTPHeaderList
*       'A':    arg = const char* C_string
*               adds a custom HTTP header to a specific response (filled out
*               by the user in the File_Info struct), may be NULL

*
*	Return : int;
*		0 - On Success
*		UPNP_E_OUTOF_MEMORY
*		UPNP_E_INVALID_URL;
*
*	Note :
************************************************************************/
int
http_MakeMessage( INOUT membuffer * buf,
                  IN int http_major_version,
                  IN int http_minor_version,
                  IN const char *fmt,
                  ... )
{
    char c;
    char *s = NULL;
    size_t num;
    off_t bignum;
    size_t length;
    time_t *loc_time;
    time_t curr_time;
    struct tm *date;
    char *start_str,
     *end_str;
    int status_code;
    const char *status_msg;
    http_method_t method;
    const char *method_str;
    const char *url_str;
    const char *temp_str;
    uri_type url;
    uri_type *uri_ptr;
    int error_code = UPNP_E_OUTOF_MEMORY;
    userHTTPHeaderList *pCurUserHTTPHeaderList;

    va_list argp;
    char tempbuf[200];
    const char *weekday_str = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
    const char *month_str = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0"
        "Jul\0Aug\0Sep\0Oct\0Nov\0Dec";

    va_start( argp, fmt );

    while( ( c = *fmt++ ) != 0 ) {

        if( c == 's' )          // C string
        {
            s = ( char * )va_arg( argp, char * );

            assert( s );

            //DBGONLY(UpnpPrintf(UPNP_ALL,HTTP,__FILE__,__LINE__,"Adding a string : %s\n", s);) 
            if( membuffer_append( buf, s, strlen( s ) ) != 0 ) {
                goto error_handler;
            }
        } 
        else if( c == 'A' )          // C string
        {
            s = ( char * )va_arg( argp, char * );

            if (s)
            {
                //DBGONLY(UpnpPrintf(UPNP_ALL,HTTP,__FILE__,__LINE__,"Adding a string : %s\n", s);) 
                if( membuffer_append( buf, s, strlen( s ) ) != 0 ) 
                {
                    goto error_handler;
                }
                if( membuffer_append( buf, "\r\n", 2 ) != 0 ) 
                {
                    goto error_handler;
                }

            }
        }
        else if( c == 'K' )   // Add Chunky header
        {
            if( membuffer_append
                ( buf, "TRANSFER-ENCODING: chunked\r\n",
                  strlen( "Transfer-Encoding: chunked\r\n" ) ) != 0 ) {
                goto error_handler;
            }
        } else if( c == 'G' )   // Add Range header
        {
            struct SendInstruction *RespInstr;
            RespInstr =
                ( struct SendInstruction * )va_arg( argp,
                                                    struct SendInstruction
                                                    * );
            assert( RespInstr );
            // connection header
            if( membuffer_append
                ( buf, RespInstr->RangeHeader,
                  strlen( RespInstr->RangeHeader ) ) != 0 ) {
                goto error_handler;
            }

        } else if( c == 'b' )   // mem buffer
        {
            s = ( char * )va_arg( argp, char * );

            //DBGONLY(UpnpPrintf(UPNP_ALL,HTTP,__FILE__,__LINE__,"Adding a char Buffer starting with: %c\n", s[0]);)
            assert( s );
            length = ( size_t ) va_arg( argp, size_t );
            if( membuffer_append( buf, s, length ) != 0 ) {
                goto error_handler;
            }
        }

        else if( c == 'c' )     // crlf
        {
            if( membuffer_append( buf, "\r\n", 2 ) != 0 ) {
                goto error_handler;
            }
        }

        else if( c == 'd' )     // integer
        {
            num = ( int )va_arg( argp, int );

            sprintf( tempbuf, "%d", (int)num );
            if( membuffer_append( buf, tempbuf, strlen( tempbuf ) ) != 0 ) {
                goto error_handler;
            }
        }

        else if( c == 'h' )     // off_t
        {
            bignum = ( off_t )va_arg( argp, off_t );

            sprintf( tempbuf, "%"PRId64, (int64_t)bignum );

            if( membuffer_append( buf, tempbuf, strlen( tempbuf ) ) != 0 ) {
                goto error_handler;
            }
        }

        else if( c == 't' || c == 'D' ) // date
        {
            if( c == 'D' ) {
                // header
                start_str = "DATE: ";
                end_str = "\r\n";
                curr_time = time( NULL );
                date = gmtime( &curr_time );
            } else {
                // date value only
                start_str = end_str = "";
                loc_time = ( time_t * ) va_arg( argp, time_t * );
                assert( loc_time );
                date = gmtime( loc_time );
            }

            sprintf( tempbuf, "%s%s, %02d %s %d %02d:%02d:%02d GMT%s",
                     start_str,
                     &weekday_str[date->tm_wday * 4], date->tm_mday,
                     &month_str[date->tm_mon * 4], date->tm_year + 1900,
                     date->tm_hour, date->tm_min, date->tm_sec, end_str );

            if( membuffer_append( buf, tempbuf, strlen( tempbuf ) ) != 0 ) {
                goto error_handler;
            }
        }

        else if( c == 'C' ) {
            if( ( http_major_version > 1 ) ||
                ( http_major_version == 1 && http_minor_version == 1 )
                 ) {
                // connection header
                if( membuffer_append_str( buf, "CONNECTION: close\r\n" ) !=
                    0 ) {
                    goto error_handler;
                }
            }
        }

        else if( c == 'N' ) {
            // content-length header
            bignum = ( off_t )va_arg( argp, off_t );

            assert( bignum >= 0 );
            if( http_MakeMessage
                ( buf, http_major_version, http_minor_version, "shc",
                  "CONTENT-LENGTH: ", bignum ) != 0 ) {
                goto error_handler;
            }
        }

        else if( c == 'S' || c == 'U' ) {
            // SERVER or USER-AGENT header

            temp_str = ( c == 'S' ) ? "SERVER: " : "USER-AGENT: ";
            get_sdk_info( tempbuf );
            if( http_MakeMessage
                ( buf, http_major_version, http_minor_version, "ss",
                  temp_str, tempbuf ) != 0 ) {
                goto error_handler;
            }
        }

	else if( c == 'H' )          // header list 
        {
            pCurUserHTTPHeaderList = 
                ( userHTTPHeaderList * )va_arg( argp, userHTTPHeaderList * );

            if (pCurUserHTTPHeaderList != NULL)
            {
                while (pCurUserHTTPHeaderList != NULL)
                {
                    if( membuffer_append
                        ( buf, pCurUserHTTPHeaderList->header, 
                          strlen( pCurUserHTTPHeaderList->header) ) != 0 ) 
                    {
                        goto error_handler;
                    }

                    if( membuffer_append( buf, "\r\n", 2 ) != 0 ) 
                    {
                        goto error_handler;
                    }

                    pCurUserHTTPHeaderList = pCurUserHTTPHeaderList->next;
                }
            }
        }
        else if( c == 'R' ) {
            // response start line
            //   e.g.: 'HTTP/1.1 200 OK'
            //

            // code
            status_code = ( int )va_arg( argp, int );

            assert( status_code > 0 );
            sprintf( tempbuf, "HTTP/%d.%d %d ",
                     http_major_version, http_minor_version, status_code );

            // str
            status_msg = http_get_code_text( status_code );
            if( http_MakeMessage
                ( buf, http_major_version, http_minor_version, "ssc",
                  tempbuf, status_msg ) != 0 ) {
                goto error_handler;
            }
        }

        else if( c == 'B' ) {
            // body of a simple reply
            // 

            status_code = ( int )va_arg( argp, int );

            sprintf( tempbuf, "%s%d %s%s",
                     "<html><body><h1>",
                     status_code, http_get_code_text( status_code ),
                     "</h1></body></html>" );
            num = strlen( tempbuf );

            if( http_MakeMessage( buf, http_major_version, http_minor_version, "NTAcs", (off_t)num, // content-length
                                  "text/html; charset=UTF-8",  // content-type
                                  gUserHTTPHeaders.buf,
                                  tempbuf ) != 0 )  // body
            {
                goto error_handler;
            }
        }

        else if( c == 'Q' ) {
            // request start line
            // GET /foo/bar.html HTTP/1.1\r\n
            //

            method = ( http_method_t ) va_arg( argp, http_method_t );
            method_str = method_to_str( method );
            url_str = ( const char * )va_arg( argp, const char * );
            num = ( size_t )va_arg( argp, size_t );   // length of url_str

            if( http_MakeMessage( buf, http_major_version, http_minor_version, "ssbsdsdc", method_str,  // method
                                  " ", url_str, (size_t)num,    // url
                                  " HTTP/", http_major_version, ".",
                                  http_minor_version ) != 0 ) {
                goto error_handler;
            }
        }

        else if( c == 'q' ) {
            // request start line and HOST header

            method = ( http_method_t ) va_arg( argp, http_method_t );

            uri_ptr = ( uri_type * ) va_arg( argp, uri_type * );
            assert( uri_ptr );
            if( http_FixUrl( uri_ptr, &url ) != 0 ) {
                error_code = UPNP_E_INVALID_URL;
                goto error_handler;
            }

            if( http_MakeMessage
                ( buf, http_major_version, http_minor_version, "Q" "sbc",
                  method, url.pathquery.buff, (size_t)url.pathquery.size, "HOST: ",
                  url.hostport.text.buff, (size_t)(url.hostport.text.size) ) != 0 ) {
                goto error_handler;
            }
        }

        else if( c == 'T' ) {
            // content type header
            temp_str = ( const char * )va_arg( argp, const char * );    // type/subtype format

            if( http_MakeMessage
                ( buf, http_major_version, http_minor_version, "ssc",
                  "CONTENT-TYPE: ", temp_str ) != 0 ) {
                goto error_handler;
            }
        }
        else {
            assert( 0 );
        }
    }

    return 0;

  error_handler:
    va_end( argp );
    membuffer_destroy( buf );
    return error_code;
}

/************************************************************************
*	Function :	http_CalcResponseVersion
*
*	Parameters :
*		IN int request_major_vers :		Request major version
*		IN int request_minor_vers :		Request minor version
*		OUT int* response_major_vers :	Response mojor version
*		OUT int* response_minor_vers :	Response minor version
*
*	Description :	Calculate HTTP response versions based on the request
*		versions.
*
*	Return :	void
*
*	Note :
************************************************************************/
void
http_CalcResponseVersion( IN int request_major_vers,
                          IN int request_minor_vers,
                          OUT int *response_major_vers,
                          OUT int *response_minor_vers )
{
    if( ( request_major_vers > 1 ) ||
        ( request_major_vers == 1 && request_minor_vers >= 1 )
         ) {
        *response_major_vers = 1;
        *response_minor_vers = 1;
    } else {
        *response_major_vers = request_major_vers;
        *response_minor_vers = request_minor_vers;
    }
}

/************************************************************************
* Function: MakeGetMessageEx												
*																		
* Parameters:															
*	const char *url_str ;	String as a URL
*	membuffer *request ;	Buffer containing the request									
*	uri_type *url ; 		URI object containing the scheme, path 
*							query token, etc.
*																		
* Description: Makes the message for the HTTP GET method				
*																		
* Returns:																
*	UPNP_E_INVALID_URL													
* 	Error Codes returned by http_MakeMessage							
*	UPNP_E_SUCCESS														
************************************************************************/
int
MakeGetMessageEx( const char *url_str,
                  membuffer * request,
                  uri_type * url,
                  struct SendInstruction *pRangeSpecifier )
{
    int errCode = UPNP_E_SUCCESS;
    char *urlPath = NULL;
    int hostlen = 0;
    char *hoststr,
     *temp;

    do {
        DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
                             "DOWNLOAD URL : %s\n", url_str );
             )

            if( ( errCode = http_FixStrUrl( ( char * )url_str,
                                            strlen( url_str ),
                                            url ) ) != UPNP_E_SUCCESS ) {
            break;
        }
        // make msg
        membuffer_init( request );

        urlPath = alloca( strlen( url_str ) + 1 );
        if( !urlPath ) {
            errCode = UPNP_E_OUTOF_MEMORY;
            break;
        }

        memset( urlPath, 0, strlen( url_str ) + 1 );

        strcpy( urlPath, url_str );

        hoststr = strstr( urlPath, "//" );
        if( hoststr == NULL ) {
            errCode = UPNP_E_INVALID_URL;
            break;
        }

        hoststr += 2;
        temp = strchr( hoststr, '/' );
        if( temp == NULL ) {
            errCode = UPNP_E_INVALID_URL;
            break;
        }

        *temp = '\0';
        hostlen = strlen( hoststr );
        *temp = '/';

        DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
                             "HOSTNAME : %s Length : %d\n", hoststr,
                             hostlen );
             )

            errCode = http_MakeMessage( request,
                                        1,
                                        1,
                                        "QsbcGDCUc",
                                        HTTPMETHOD_GET,
                                        url->pathquery.buff,
                                        (size_t)(url->pathquery.size),
                                        "HOST: ",
                                        hoststr,
                                        (size_t)hostlen, pRangeSpecifier );

        if( errCode != 0 ) {
            DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
                                 "HTTP Makemessage failed\n" );
                 )
                membuffer_destroy( request );
            return errCode;
        }
    } while( 0 );

    DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
                         "HTTP Buffer:\n %s\n" "----------END--------\n",
                         request->buf );
         )

        return errCode;
}

#define SIZE_RANGE_BUFFER 50

/************************************************************************
*	Function :	http_OpenHttpGetEx
*
*	Parameters :
*		IN const char *url_str :	String as a URL
*		IN OUT void **Handle :		Pointer to buffer to store HTTP
*									post handle
*		IN OUT char **contentType :	Type of content
*		OUT off_t *contentLength :	length of content
*		OUT int *httpStatus :		HTTP status returned on receiving a
*									response message
*		IN int timeout :			time out value
*
*	Description :	Makes the HTTP GET message, connects to the peer, 
*		sends the HTTP GET request, gets the response and parses the 
*		response.
*
*	Return : int;
*		UPNP_E_SUCCESS - On Success ;
*		UPNP_E_INVALID_PARAM - Invalid Paramters ;
*		UPNP_E_OUTOF_MEMORY ;
*		UPNP_E_SOCKET_ERROR ;
*		UPNP_E_BAD_RESPONSE ;
*
*	Note :
*
************************************************************************/
int
http_OpenHttpGetEx( IN const char *url_str,
                    IN OUT void **Handle,
                    IN OUT char **contentType,
                    OUT off_t *contentLength,
                    OUT int *httpStatus,
                    IN int lowRange,
                    IN int highRange,
                    IN int timeout )
{
    int http_error_code;
    memptr ctype;
    int tcp_connection;
    membuffer request;
    http_get_handle_t *handle = NULL;
    uri_type url;
    parse_status_t status;
    int errCode = UPNP_E_SUCCESS;

    //  char rangeBuf[SIZE_RANGE_BUFFER];
    struct SendInstruction rangeBuf;

    do {
        // Checking Input parameters
        if( ( !url_str ) || ( !Handle ) ||
            ( !contentType ) || ( !httpStatus ) ) {
            errCode = UPNP_E_INVALID_PARAM;
            break;
        }
        // Initialize output parameters
        ( *httpStatus ) = 0;
        ( *Handle ) = handle;
        ( *contentType ) = NULL;
        ( *contentLength ) = 0;

        if( lowRange > highRange ) {
            errCode = UPNP_E_INTERNAL_ERROR;
            break;
        }

        memset( &rangeBuf, 0, sizeof( rangeBuf ) );
        sprintf( rangeBuf.RangeHeader, "Range: bytes=%d-%d\r\n",
                 lowRange, highRange );

        membuffer_init( &request );

        if( ( errCode = MakeGetMessageEx( url_str,
                                          &request, &url, &rangeBuf ) )
            != UPNP_E_SUCCESS ) {
            break;
        }

        handle =
            ( http_get_handle_t * ) malloc( sizeof( http_get_handle_t ) );
        if( handle == NULL ) {
            errCode = UPNP_E_OUTOF_MEMORY;
            break;
        }

        memset( handle, 0, sizeof( *handle ) );

        handle->entity_offset = 0;
        parser_response_init( &handle->response, HTTPMETHOD_GET );

        tcp_connection = socket( AF_INET, SOCK_STREAM, 0 );
        if( tcp_connection == -1 ) {
            errCode = UPNP_E_SOCKET_ERROR;
            free( handle );
            break;
        }

        if( sock_init( &handle->sock_info, tcp_connection ) !=
            UPNP_E_SUCCESS ) {
            sock_destroy( &handle->sock_info, SD_BOTH );
            errCode = UPNP_E_SOCKET_ERROR;
            free( handle );
            break;
        }

        errCode = connect( handle->sock_info.socket,
                           ( struct sockaddr * )&url.hostport.IPv4address,
                           sizeof( struct sockaddr_in ) );
        if( errCode == -1 ) {
            sock_destroy( &handle->sock_info, SD_BOTH );
            errCode = UPNP_E_SOCKET_CONNECT;
            free( handle );
            break;
        }
        // send request
        errCode = http_SendMessage( &handle->sock_info,
                                    &timeout,
                                    "b", request.buf, (size_t)request.length );

        if( errCode != UPNP_E_SUCCESS ) {
            sock_destroy( &handle->sock_info, SD_BOTH );
            free( handle );
            break;
        }

        status = ReadResponseLineAndHeaders( &handle->sock_info,
                                             &handle->response,
                                             &timeout, &http_error_code );

        if( status != PARSE_OK ) {
            errCode = UPNP_E_BAD_RESPONSE;
            free( handle );
            break;
        }

        status = parser_get_entity_read_method( &handle->response );
        if( ( status != PARSE_CONTINUE_1 ) && ( status != PARSE_SUCCESS ) ) {
            errCode = UPNP_E_BAD_RESPONSE;
            free( handle );
            break;
        }

        ( *httpStatus ) = handle->response.msg.status_code;
        errCode = UPNP_E_SUCCESS;

        if( httpmsg_find_hdr( &handle->response.msg,
                              HDR_CONTENT_TYPE, &ctype ) == NULL ) {
            *contentType = NULL;    // no content-type
        } else {
            *contentType = ctype.buf;
        }

        if( handle->response.position == POS_COMPLETE ) {
            ( *contentLength ) = 0;
        } else if( handle->response.ent_position == ENTREAD_USING_CHUNKED ) {
            ( *contentLength ) = UPNP_USING_CHUNKED;
        } else if( handle->response.ent_position == ENTREAD_USING_CLEN ) {
            ( *contentLength ) = handle->response.content_length;
        } else if( handle->response.ent_position == ENTREAD_UNTIL_CLOSE ) {
            ( *contentLength ) = UPNP_UNTIL_CLOSE;
        }

        ( *Handle ) = handle;

    } while( 0 );

    membuffer_destroy( &request );

    return errCode;
}

/************************************************************************
*	Function :	get_sdk_info
*
*	Parameters :
*		OUT char *info :	buffer to store the operating system 
*							information
*
*	Description :	Returns the server information for the operating 
*		system
*
*	Return :	XINLINE void
*
*	Note :
************************************************************************/
// 'info' should have a size of at least 100 bytes
void
get_sdk_info( OUT char *info )
{
#ifdef WIN32
 	OSVERSIONINFO versioninfo;
 	versioninfo.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
 	if (GetVersionEx(&versioninfo)!=0)
 		sprintf( info, "%d.%d.%d %d/%s, UPnP/1.0, Portable SDK for UPnP devices/"PACKAGE_VERSION"\r\n",
 				versioninfo.dwMajorVersion, versioninfo.dwMinorVersion, versioninfo.dwBuildNumber, versioninfo.dwPlatformId, versioninfo.szCSDVersion );
 	else
     *info = '\0';
#else
    int ret_code;
    struct utsname sys_info;

    ret_code = uname( &sys_info );
    if( ret_code == -1 ) {
        *info = '\0';
    }

    sprintf( info, "%s/%s, UPnP/1.0, " PACKAGE_NAME "/"
	     PACKAGE_VERSION "\r\n",
             sys_info.sysname, sys_info.release );
#endif
}
